<template>
    <a-layout class="containers">
        <a-layout-header class="bpmnHeader">
            <div class="operate">
                <a-upload accept="*.xml" :before-upload="openBpmn" :defaultFileList="[]" class="ant-btn update">
                    <a-tooltip placement="bottom">
                        <template slot="title">
                            <span>加载xml</span>
                        </template>
                        <a-button>

                            <a-icon type="folder" />

                        </a-button>
                    </a-tooltip>
                </a-upload>
                <a-tooltip placement="bottom">
                    <template slot="title">
                        <span>新建</span>
                    </template>
                    <a-button @click="newDiagram">
                        <a-icon type="plus" />
                    </a-button>
                </a-tooltip>
                <a-tooltip placement="bottom">
                    <template slot="title">
                        <span>自适应屏幕</span>
                    </template>
                    <a-button @click="fitViewport">
                        <a-icon type="drag" />
                    </a-button>
                </a-tooltip>
                <a-tooltip placement="bottom">
                    <template slot="title">
                        <span>放大</span>
                    </template>
                    <a-button @click="zoomViewport(true)">
                        <a-icon type="plus-square" />
                    </a-button>
                </a-tooltip>
                <a-tooltip placement="bottom">
                    <template slot="title">
                        <span>缩小</span>
                    </template>
                    <a-button @click="zoomViewport(false)">
                        <a-icon type="minus-square" />
                    </a-button>
                </a-tooltip>
                <a-tooltip placement="bottom">
                    <template slot="title">
                        <span>后退</span>
                    </template>
                    <a-button @click="modeler.get('commandStack').undo()">
                        <a-icon type="arrow-left" />
                    </a-button>
                </a-tooltip>
                <a-tooltip placement="bottom">
                    <template slot="title">
                        <span>前进</span>
                    </template>
                    <a-button @click="modeler.get('commandStack').undo()">
                        <a-icon type="arrow-right" />
                    </a-button>
                </a-tooltip>
            </div>
            <div class="operate">
                <a-button icon="vertical-align-bottom" @click="saveXML(true)">
                    下载xml
                </a-button>
                <a-button icon="picture" @click="saveImg('svg', true)">
                    下载svg
                </a-button>
                <a-button type="primary" @click="save">
                    保存模型
                </a-button>
            </div>
        </a-layout-header>
        <div class="section">
            <div class="canvas" ref="canvas"></div>
            <div class="properties-tool">
                <NodePanel v-if="modeler" :modeler="modeler" :users="users" :fromList="fromList" :groups="groups" @dataType="dataType">
                </NodePanel>
            </div>
        </div>
    </a-layout>
</template>
  
<script>
// 引入相关的依赖
import Modeler from 'bpmn-js/lib/Modeler'
import BpmData from './BpmData'
import NodePanel from './nodePanel/index.vue';
import getInitStr from './flowable/init'
// import flowableModdle from './activiti'
import flowableModdle from './flowable/flowable'

//汉化包
import customTranslate from "./lib/customTranslate";


export default {
    name: '',
    components: { NodePanel },
    props: {
        xml: {
            type: String,
            default: ''
        },
        users: {
            type: Array,
            default: () => []
        },
        groups: {
            type: Array,
            default: () => []
        },
        // 节点关联表单
        fromList: {
            type: Array,
            default: () => []
        },
        categorys: {
            type: Array,
            default: () => []
        },
        isView: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            modeler: null,
            taskList: [],
            zoom: 1
        }
    },
    watch: {
        xml: function (val) {
            if (val) {
                this.createNewDiagram(val)
            }
        }
    },
    mounted() {
        // 生成实例
        this.modeler = new Modeler({
            container: this.$refs.canvas,
            additionalModules: [
                {
                    translate: ['value', customTranslate]
                }
            ],
            moddleExtensions: {
                flowable: flowableModdle
            }
        })
        // 新增流程定义
        if (!this.xml) {
            this.newDiagram()
        } else {
            this.createNewDiagram(this.xml)
        }
    },
    methods: {
        newDiagram() {
            this.createNewDiagram(getInitStr())
        },
        // 让图能自适应屏幕
        fitViewport() {
            this.zoom = this.modeler.get('canvas').zoom('fit-viewport')
            const bbox = document.querySelector('.containers .viewport').getBBox()
            const currentViewbox = this.modeler.get('canvas').viewbox()
            const elementMid = {
                x: bbox.x + bbox.width / 2 - 65,
                y: bbox.y + bbox.height / 2
            }
            this.modeler.get('canvas').viewbox({
                x: elementMid.x - currentViewbox.width / 2,
                y: elementMid.y - currentViewbox.height / 2,
                width: currentViewbox.width,
                height: currentViewbox.height
            })
            this.zoom = bbox.width / currentViewbox.width * 1.8
        },
        // 放大缩小
        zoomViewport(zoomIn = true) {
            this.zoom = this.modeler.get('canvas').zoom()
            this.zoom += (zoomIn ? 0.1 : -0.1)
            this.modeler.get('canvas').zoom(this.zoom)
        },
        async createNewDiagram(data) {
            // 将字符串转换成图显示出来
            // xml = xml.replace(/</g, '&lt;')
            // xml = xml.replace(/>/g, '&gt;')
            data = data.replace(/<!\[CDATA\[(.+)]]>/g, '&lt;![CDATA[$1]]&gt;')
            try {
                await this.modeler.importXML(data)
                this.adjustPalette()
                this.fitViewport()
                // this.fillColor()
            } catch (err) {
                console.error(err.message, err.warnings)
            }
        },
        // 调整左侧工具栏排版
        adjustPalette() {
            try {
                // 获取 bpmn 设计器实例
                const canvas = this.$refs.canvas
                const djsPalette = canvas.children[0].children[1].children[4]
                const djsPalStyle = {
                    width: '130px',
                    padding: '5px',
                    background: 'white',
                    left: '20px',
                    borderRadius: 0
                }
                for (var key in djsPalStyle) {
                    djsPalette.style[key] = djsPalStyle[key]
                }
                const palette = djsPalette.children[0]
                const allGroups = palette.children
                allGroups[0].style['display'] = 'none'
                // 修改控件样式
                for (var gKey in allGroups) {
                    const group = allGroups[gKey]
                    for (var cKey in group.children) {
                        const control = group.children[cKey]
                        const controlStyle = {
                            display: 'flex',
                            justifyContent: 'flex-start',
                            alignItems: 'center',
                            width: '100%',
                            padding: '5px'
                        }
                        if (
                            control.className &&
                            control.dataset &&
                            control.className.indexOf('entry') !== -1
                        ) {
                            const controlProps = new BpmData().getControl(
                                control.dataset.action
                            )
                            control.innerHTML = `<div style='font-size: 14px;font-weight:500;margin-left:15px;'>${controlProps['title']
                                }</div>`
                            for (var csKey in controlStyle) {
                                control.style[csKey] = controlStyle[csKey]
                            }
                        }
                    }
                }
            } catch (e) {
                console.log(e)
            }
        },
        fillColor() {
            const canvas = this.modeler.get('canvas')
            this.modeler._definitions.rootElements[0].flowElements.forEach(n => {
                if (n.$type === 'bpmn:UserTask') {
                    const completeTask = this.taskList.find(m => m.key === n.id) || { completed: true }
                    const todoTask = this.taskList.find(m => !m.completed)
                    const endTask = this.taskList[this.taskList.length - 1]
                    if (completeTask) {
                        canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
                        n.outgoing?.forEach(nn => {
                            const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
                            if (targetTask) {
                                canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
                            } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
                                // canvas.addMarker(nn.id, 'highlight');
                                canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo')
                                canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo')
                            } else if (nn.targetRef.$type === 'bpmn:EndEvent') {
                                if (!todoTask && endTask.key === n.id) {
                                    canvas.addMarker(nn.id, 'highlight')
                                    canvas.addMarker(nn.targetRef.id, 'highlight')
                                }
                                if (!completeTask.completed) {
                                    canvas.addMarker(nn.id, 'highlight-todo')
                                    canvas.addMarker(nn.targetRef.id, 'highlight-todo')
                                }
                            }
                        })
                    }
                } else if (n.$type === 'bpmn:ExclusiveGateway') {
                    n.outgoing.forEach(nn => {
                        const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
                        if (targetTask) {
                            canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
                        }
                    })
                }
                if (n.$type === 'bpmn:StartEvent') {
                    n.outgoing.forEach(nn => {
                        const completeTask = this.taskList.find(m => m.key === nn.targetRef.id)
                        if (completeTask) {
                            canvas.addMarker(nn.id, 'highlight')
                            canvas.addMarker(n.id, 'highlight')
                            return
                        }
                    })
                }
            })
        },
        // 对外 api
        getProcess() {
            const element = this.getProcessElement()
            return {
                id: element.id,
                name: element.name,
                category: element.$attrs['flowable:processCategory']
            }
        },
        getProcessElement() {
            return this.modeler.getDefinitions().rootElements[0]
        },
        async saveXML(download = false) {
            try {
                let { xml } = await this.modeler.saveXML({ format: true })
                xml = xml.replace(/&lt;/g, '<')
                xml = xml.replace(/&gt;/g, '>')
                if (download) {
                    this.downloadFile(`${this.getProcessElement().name}.bpmn20.xml`, xml, 'application/xml')
                }
                return xml
            } catch (err) {
                console.log(err)
            }
        },
        async saveImg(type = 'svg', download = false) {
            try {
                const { svg } = await this.modeler.saveSVG({ format: true })
                if (download) {
                    this.downloadFile(this.getProcessElement().name, svg, 'image/svg+xml')
                }
                return svg
            } catch (err) {
                console.log(err)
            }
        },
        async save() {
            const process = this.getProcess()
            const xml = await this.saveXML()
            const svg = await this.saveImg()
            const result = { process, xml, svg }
            this.$emit('save', result)
            window.parent.postMessage(result, '*')
        },
        openBpmn(file) {
            const reader = new FileReader()
            reader.readAsText(file, 'utf-8')
            reader.onload = () => {
                this.createNewDiagram(reader.result)
            }
            return false
        },
        downloadFile(filename, data, type) {
            var a = document.createElement('a')
            var url = window.URL.createObjectURL(new Blob([data], { type: type }))
            a.href = url
            a.download = filename
            a.click()
            window.URL.revokeObjectURL(url)
        },
        /** 获取数据类型 */
        dataType(data) {
            this.$emit('dataType', data)
        }
    }
}
</script>
  
<style lang="less" scoped>
.bpmnHeader {
    display: flex;
    padding: 10px 0px;
    justify-content: space-between;
    border-bottom: 1px solid rgb(218, 218, 218);
    max-height: 50px;
    background-color: #ffffff;
}


.containers {
    background-color: #ffffff;
    padding: 10px 10px;
    width: 100%;
    height: 800px;
    position: relative;
}

.canvas {
    width: 100%;
    height: 100%;
}

.properties-tool {
    width: 400px;
    height: 650px;
    overflow: auto;
    // background-color: rgb(240, 242, 245);
    background-color: rgb(250 250 250);
    border: 1px solid #e0e0e0;
    margin: -0.1px;

}

.section {
    display: flex;
}

.operate {
    display: flex;

    .ant-btn+.ant-btn {
        margin-left: 10px;
    }
}

span {
    display: flex;
}

/deep/ .ant-upload-list {
    display: none;
}

.update {
    border: none;
    padding: 0;
}
</style>